import json
import networkx as nx


class EntityRelationGraph:
    def __init__(self, config_data):
        """
        初始化加权关系图
        :param config_data: 包含 'tables' 和 'relations' 的字典
        """
        self.G = nx.DiGraph()
        self.entities = {}  # name -> table config
        self.relations = {}  # (from, to) -> relation config
        self.time_ranges = config_data["time_range"]  # time_range
        self.extra_datas = config_data["extra_datas"]  # day -> {"file_name":[]}
        self._build_weighted_graph(config_data)

    def _build_weighted_graph(self, config_data):
        # 注册所有实体
        for table in config_data["tables"]:
            name = table["name"]
            self.entities[name] = table
            self.G.add_node(name)

        # 注册所有关系并计算权重
        for rel in config_data["relations"]:
            from_ent = rel["from"]
            to_ent = rel["to"]
            rel_on = rel["on"]
            # 计算权重：关联字段的数量
            weight = len(rel_on)

            # 存储关系信息和权重
            self.relations[(from_ent, to_ent)] = rel
            self.G.add_edge(from_ent, to_ent, weight=weight)

        # 模板存入对应的实体中
        templates = config_data["templates"] or {}
        # 迭代entities
        for name, entity in self.entities.items():
            entity["templates"] = templates.get(name, [])

    def _normalize_rel_on(rel_on_list):
        # 提取 (left, right) 对，并按 left_field 排序确保一致性
        pairs = [(item["left_field"], item["right_field"]) for item in rel_on_list]
        pairs.sort(key=lambda x: x[0])  # 按 left_field 排序
        return tuple(pairs)

    def get_neighbors(self, entity_name):
        """获取直接关联的下游实体及其权重"""
        if entity_name not in self.G:
            return []

        neighbors = []
        for successor in self.G.successors(entity_name):
            weight = self.G[entity_name][successor]["weight"]
            neighbors.append((successor, weight))
        return neighbors

    def get_reverse_neighbors(self, entity_name):
        """获取直接关联的上游实体及其权重"""
        if entity_name not in self.G:
            return []

        neighbors = []
        for predecessor in self.G.predecessors(entity_name):
            weight = self.G[predecessor][entity_name]["weight"]
            neighbors.append((predecessor, weight))
        return neighbors

    def get_edge_weight(self, from_ent, to_ent):
        """获取两个实体之间的边权重"""
        if self.G.has_edge(from_ent, to_ent):
            return self.G[from_ent][to_ent]["weight"]
        return None

    def get_graph(self):
        """返回底层图对象"""
        return self.G

    def get_proper_build_order(self):
        """
        推举一个合理的构建顺序，基于以下规则：
        1. 出度最小，入度最大优先
        2. 出度为0且入度为0：第一阶梯节点，优先构建
        3. 出度为0且入度不为0：不依赖其他节点，优先构建，按入度从大到小排序
        4. 出度不为0且入度不为0：依赖其他节点，等前置节点构建完成后构建
        5. 出度不为0且入度为0：依赖其他节点但无节点依赖它，最后构建
        6. 循环依赖时：断开循环，优先构建入度最大的节点
        7. 出度和入度相同时：按权重从高到低排序
        """
        # 预先计算所有节点的出度、入度和权重信息
        node_info = {}
        for node in self.G.nodes():
            out_degree = self.G.out_degree(node)
            in_degree = self.G.in_degree(node)

            # 计算节点的总权重（所有出边权重之和）
            total_weight = sum(
                self.G[node][succ]["weight"] for succ in self.G.successors(node)
            )

            node_info[node] = {
                "out_degree": out_degree,
                "in_degree": in_degree,
                "total_weight": total_weight,
                "type": self._get_node_type(out_degree, in_degree),
            }

        print(f"节点信息: {node_info}")

        remaining_nodes = list(self.G.nodes())
        build_order = []

        # 第一步：处理出度为0且入度为0的节点（第一阶梯节点）
        type1_nodes = [
            node for node in remaining_nodes if node_info[node]["type"] == "type1"
        ]
        if type1_nodes:
            type1_nodes.sort(
                key=lambda x: (-node_info[x]["total_weight"])
            )  # 权重从高到低
            build_order.extend(type1_nodes)
            remaining_nodes = [
                node for node in self.G.nodes() if node not in set(type1_nodes)
            ]

        # 第二步：处理入度为0且出度不为0的节点（优先构建，按入度从大到小）
        type2_nodes = [
            node for node in remaining_nodes if node_info[node]["type"] == "type2"
        ]
        if type2_nodes:
            type2_nodes.sort(
                key=lambda x: (
                    -node_info[x]["out_degree"],
                    -node_info[x]["total_weight"],
                )
            )
            build_order.extend(type2_nodes)
            remaining_nodes = [
                node for node in remaining_nodes if node not in set(type2_nodes)
            ]

        # 第三步：拓扑排序构建其他节点
        iteration_count = 0
        max_iterations = len(self.G.nodes()) * 3  # 安全限制

        while remaining_nodes and iteration_count < max_iterations:
            iteration_count += 1

            # 找出当前可以构建的节点（所有前置节点都已构建）
            available_nodes = []
            for node in remaining_nodes:
                predecessors = set(self.G.predecessors(node))
                print(
                    f"节点 {node} 的前置节点: {predecessors}, 已构建节点: {set(build_order)}"
                )
                if predecessors.issubset(set(build_order)):
                    available_nodes.append(node)

            if available_nodes:
                # 对可用节点排序：出度最小，入度最大，权重最高
                available_nodes.sort(
                    key=lambda x: (
                        node_info[x]["in_degree"],  # 出度最小
                        -node_info[x]["out_degree"],  # 入度最大
                        -node_info[x]["total_weight"],  # 权重最高
                    )
                )
                next_node = available_nodes[0]
                build_order.append(next_node)
                remaining_nodes.remove(next_node)
                print(
                    f"构建节点: {next_node} (入度: {node_info[next_node]['in_degree']}, "
                    f"出度: {node_info[next_node]['out_degree']}, "
                    f"权重: {node_info[next_node]['total_weight']})"
                )
            else:
                # 处理循环依赖：优先构建入度最大的节点
                if remaining_nodes:
                    # 选择入度最大的节点（如果有多个，选择权重最高的）
                    next_node = max(
                        remaining_nodes,
                        key=lambda x: (
                            node_info[x]["out_degree"],
                            node_info[x]["total_weight"],
                        ),
                    )

                    print(
                        f"警告：检测到循环依赖，强制构建节点: {next_node} "
                        f"(入度: {node_info[next_node]['out_degree']}, "
                        f"权重: {node_info[next_node]['total_weight']})"
                    )

                    build_order.append(next_node)
                    remaining_nodes.remove(next_node)

        # 第四步：处理剩余节点（主要是出度不为0且入度为0的节点，最后构建）
        if remaining_nodes:
            # 对这些节点排序：出度最小，权重最高
            remaining_list = list(remaining_nodes)
            remaining_list.sort(
                key=lambda x: (
                    node_info[x]["out_degree"],  # 出度最小
                    -node_info[x]["total_weight"],  # 权重最高
                )
            )
            build_order.extend(remaining_list)
            print(f"信息：{len(remaining_list)}个节点在最后阶段构建")

        return build_order

    def _get_node_type(self, out_degree, in_degree):
        """根据出度和入度确定节点类型"""
        if in_degree == 0 and out_degree == 0:
            return "type1"  # 第一阶梯节点
        elif in_degree == 0 and out_degree > 0:
            return "type2"  # 不依赖其他节点
        elif in_degree > 0 and out_degree > 0:
            return "type3"  # 依赖其他节点
        elif in_degree > 0 and out_degree == 0:
            return "type4"  # 依赖其他节点但无节点依赖它
        else:
            return "unknown"

    def get_graph_summary(self):
        """
        获取图的整体摘要信息
        """
        summary = {
            "total_nodes": self.G.number_of_nodes(),
            "total_edges": self.G.number_of_edges(),
            "nodes": list(self.G.nodes()),
            "edges": list(self.G.edges()),
            "node_degrees": {
                node: {
                    "in_degree": self.G.in_degree(node),
                    "out_degree": self.G.out_degree(node),
                }
                for node in self.G.nodes()
            },
        }
        return summary


if __name__ == "__main__":
    config_json = """
{
  "tables": [
    {
      "name": "A",
      "struts": [
        {
          "field": "FIELD",
          "type": "str"
        },
        {
          "field": "FIELD2",
          "type": "str"
        },
        {
          "field": "CREATE_TIME",
          "type": "datetime"
        },
        {
          "field": "LOGID",
          "type": "str"
        }
      ],
      "desc": ""
    },
    {
      "name": "B",
      "struts": [
        {
          "field": "FIELD",
          "type": "str"
        },
        {
          "field": "FIELD2",
          "type": "str"
        }
      ],
      "desc": ""
    },
    {
      "name": "C",
      "struts": [
        {
          "field": "FIELD",
          "type": "str"
        },
        {
          "field": "FIELD2",
          "type": "str"
        }
      ],
      "desc": ""
    },
    {
      "name": "D",
      "struts": [
        {
          "field": "FIELD",
          "type": "str"
        },
        {
          "field": "FIELD2",
          "type": "str"
        }
      ],
      "desc": ""
    },
    {
      "name": "E",
      "struts": [
        {
          "field": "FIELD",
          "type": "str"
        },
        {
          "field": "CREATE_TIME",
          "type": "datetime"
        }
      ],
      "desc": ""
    },
    {
      "name": "LOG",
      "struts": [
        {
          "field": "ID",
          "type": "str"
        },
        {
          "field": "CREATE_TIME",
          "type": "datetime"
        }
      ],
      "desc": ""
    },
    {
      "name": "F",
      "struts": [
        {
          "field": "ID",
          "type": "str"
        },
        {
          "field": "CREATE_TIME",
          "type": "datetime"
        }
      ],
      "desc": ""
    }
  ],
  "relations": [
    {
      "from": "F",
      "to": "LOG",
      "on": [
        {
          "left_field": "CREATE_TIME",
          "right_field": "CREATE_TIME"
        }
      ]
    },
    {
      "from": "LOG",
      "to": "A",
      "on": [
        {
          "left_field": "LOGID",
          "right_field": "ID"
        }
      ]
    },
    {
      "from": "A",
      "to": "B",
      "on": [
        {
          "left_field": "FIELD",
          "right_field": "FIELD"
        },
        {
          "left_field": "FIELD2",
          "right_field": "FIELD2"
        }
      ]
    },
    {
      "from": "A",
      "to": "C",
      "on": [
        {
          "left_field": "FIELD",
          "right_field": "FIELD"
        },
        {
          "left_field": "FIELD2",
          "right_field": "FIELD2"
        }
      ]
    },
    {
      "from": "C",
      "to": "B",
      "on": [
        {
          "left_field": "FIELD",
          "right_field": "FIELD"
        }
      ]
    },
    {
      "from": "A",
      "to": "D",
      "on": [
        {
          "left_field": "FIELD",
          "right_field": "FIELD"
        },
        {
          "left_field": "FIELD2",
          "right_field": "FIELD2"
        }
      ]
    },
    {
      "from": "D",
      "to": "E",
      "on": [
        {
          "left_field": "FIELD",
          "right_field": "FIELD"
        }
      ]
    }
  ]
}
    """
    entry_graph = EntityRelationGraph(json.loads(config_json))
    result = entry_graph.get_proper_build_order()
    print(f"正确的构建顺序: {result}")

    # 打印图摘要信息
    print("\n" + "=" * 80)
    print("图结构摘要")
    print("=" * 80)
    summary = entry_graph.get_graph_summary()
    print(f"总节点数: {summary['total_nodes']}")
    print(f"总边数: {summary['total_edges']}")
    print(f"所有节点: {summary['nodes']}")
    print(f"所有边: {summary['edges']}")

    print("\n节点度信息:")
    for node, degrees in summary["node_degrees"].items():
        print(f"  {node}: 入度={degrees['in_degree']}, 出度={degrees['out_degree']}")
